#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include "bf.h"

bf_stream bf_stream_new (int fd) {
  bf_stream this = (bf_stream) malloc (sizeof (struct _bitiob));
  this->base = (char*) malloc (sizeof (char) * BUFFSIZE / 8);
  this->fd = fd;
  this->ptr = this->base;
  this->count = 0;
  return this;
}

bf_stream bf_open (char *path, int mode) {
  int fd = open (path, mode, 0644);
  if (fd == -1)
    return NULL;
  return bf_stream_new (fd);  
}

int bf_close (bf_stream bf) {
  int ret;
  bf_flush (bf);
  ret = close (bf->fd);
  free (bf);
  return ret;
}

void bf_fillbuff (bf_stream bf) {
  bf->count = read (bf->fd, bf->base, BUFFSIZE / 8);
  if (bf->count == -1) {
    perror ("bf_fillbuf");
    exit (EXIT_FAILURE);
  }
  bf->ptr = bf->base;
  bf->count *= 8;
}

int bf_flush (bf_stream bf) {
  int r = 0;
  if (bf->count % 8) {
    fprintf (stderr, "flushing bf. count : %d, data %s\n", bf->count, bf->base);
  }
  bf->count /= 8;
  while (bf->count) {
    r = write (bf->fd, bf->base, bf->count);
    if (r == -1) {
      perror ("bf_flush");
      return -1;
    }
    bf->count -= r;
  }
  return r;
}

int bf_putbit (bf_stream bf, int b) {
  int q, r;
  q = bf->count / 8; r = 7 - bf->count % 8;
  if (b) bf->base[q] |= (1 << r);
  else bf->base[q] &= ~(1 << r);
  if (++bf->count == BUFFSIZE)
    bf_flush (bf);
  return b;
}

int bf_getbit (bf_stream bf) {
  int r;
  if (bf->count == 0)
    bf_fillbuff (bf);
  if (bf->count == 0) return EOF;
  bf->count--;
  r = bf->count % 8;
  
  return r ? ((*bf->ptr >> r) & 1) : ((*(bf->ptr++) >> r) & 1);
}

int bf_putchar (bf_stream bf, int c) {
  int i;
  for (i = 7; i >= 0; i--) bf_putbit (bf, (c >> i) & 1);
  return c;
}

int bf_getchar (bf_stream bf) {
  int i, b, ret = 0;
  for (i = 7; i >= 0; i--) {
    b = bf_getbit (bf);
    if (b == EOF) return EOF;
    ret += b * pow (2, i);
  }
  return ret;
}

int bf_isaligned (bf_stream bf) {
  return (bf->count % 8) == 0;
}

int bf_align (bf_stream bf) {
  int i;
  if (bf_isaligned (bf)) return 0;
  for (i = 8 - (bf->count % 8); i > 0; i--)
    bf_putbit (bf, 0);
  return 0;
}
